package com.rw.tool.util.basic;

import com.rw.tool.util.basic.exception.ArrayLengthIllegalException;
import com.rw.tool.util.basic.exception.UnknownEncodingException;
import com.rw.tool.util.io.SerializationUtil;
import com.rw.tool.util.string.StringUtil;

import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

/**
 * 字节工具类
 */
public class ByteUtil {
    /**
     * 字节标志位数组
     */
    private static final byte[] BIT_MASK;

    /**
     * 数组长度
     */
    private static final int BIT_MASK_LEN = 8;

    static {
        BIT_MASK = new byte[BIT_MASK_LEN];

        // 初始化数组的8个值
        for (int i = 0; i < BIT_MASK_LEN; i++) {
            BIT_MASK[i] = (byte) (1 << BIT_MASK_LEN - 1 - i);
        }
    }

    /**
     * 布尔值字节长度
     */
    public static final int BOOLEAN_BYTE_LENGTH = 1;

    /**
     * 字符字节长度
     */
    public static final int CHAR_BYTE_LENGTH = 2;

    /**
     * 短整型字节长度
     */
    public static final int SHORT_BYTE_LENGTH = 2;

    /**
     * 整型字节长度
     */
    public static final int INT_BYTE_LENGTH = 4;

    /**
     * 单精度浮点型字节长度
     */
    public static final int FLOAT_BYTE_LENGTH = 4;

    /**
     * 长整型字节长度
     */
    public static final int LONG_BYTE_LENGTH = 8;

    /**
     * 双精度浮点型字节长度
     */
    public static final int DOUBLE_BYTE_LENGTH = 8;

    /**
     * 获取字节掩码
     *
     * @param index 第几位的字节掩码，范围 0-7
     * @return 字节掩码
     */
    public static byte getBitMask(int index) {
        return BIT_MASK[index];
    }

    /**
     * 获取字节掩码数组的长度
     */
    public static int getBitMaskLength() {
        return BIT_MASK_LEN;
    }

    /**
     * 获取字节的第几位
     * 判断这一位是否为 1
     *
     * @param value 被判断的数字
     * @param index 位数，范围 0-7
     * @return 字节的位数是否为 1
     */
    public static boolean hasBit(byte value, int index) {
        return (BIT_MASK[index] & value) != 0;
    }

    /**
     * 获取字节的第几位
     * 判断这一位是否为 1并返回
     *
     * @param value 被判断的数字
     * @param index 位数，范围 0-7
     * @return 0 或 1
     */
    public static byte getBitNum(byte value, int index) {
        if (hasBit(value, index)) {
            return 1;
        } else {
            return 0;
        }
    }

    /**
     * 设置字节的第几位，返回新的数字
     *
     * @param value 被设置的数字
     * @param index 位数，范围 0-7
     * @param bit   是否设置为 1
     * @return 被设置的新数字
     */
    public static byte setBit(byte value, int index, boolean bit) {
        if (bit) {
            return (byte) (value | BIT_MASK[index]);
        } else {
            return (byte) (value & ~BIT_MASK[index]);
        }
    }

    /**
     * 设置字节的第几位，返回新的数字
     *
     * @param value 被设置的数字
     * @param index 位数，范围 0-7
     * @param bit   是否设置为 1
     * @return 被设置的新数字
     */
    public static byte setBit(byte value, int index, byte bit) {
        return setBit(value, index, bit != 0);
    }

    /**
     * 把byte转化为八位二进制字符串
     *
     * @param value 数值
     * @return 八位二进制字符串
     */
    public static String toBinaryString(byte value) {
        StringBuilder result = new StringBuilder();

        // 遍历所有位数，从高位判断到低位
        for (int i = 0; i < BIT_MASK_LEN; i++) {
            if (hasBit(value, i)) {
                result.append("1");
            } else {
                result.append("0");
            }
        }

        // 反转
        result.reverse();

        return result.toString();
    }

    /**
     * 解析二进制字符串
     *
     * @param valueStr 二进制字符串
     * @return 字节
     */
    public static byte parseBinaryString(String valueStr) {
        // 判断字符串非空
        if (StringUtil.isBlank(valueStr)) {
            final String errorMsg = "valueStr can not be empty!";
            throw new NumberFormatException(errorMsg);
        }

        // 判断是否符合正则表达式
        final String regex = "[01_]{1,8}";
        if (!valueStr.matches(regex)) {
            final String errorMsg = "valueStr must match [01_]{1,8}!";
            throw new NumberFormatException(errorMsg);
        }

        // 声明结果
        byte result = 0;

        // 倒序遍历这个字符数组
        char[] chars = valueStr.replace("_", "").toCharArray();
        for (int i = chars.length - 1; i >= 0; i--) {
            // 获得这一位的数字
            byte bit = parseChar(chars[i]);

            // 设置对应位置的字节
            int index = chars.length - 1 - i;
            result = setBit(result, index, bit);
        }

        return result;
    }

    /**
     * 解析单个字符为0或1
     *
     * @param c 单个字符
     * @return 解析出来的byte
     */
    private static byte parseChar(char c) {
        switch (c) {
            case '0':
                return 0;
            case '1':
                return 1;
            default:
                final String errorMsg = "unknown char : " + c;
                throw new NumberFormatException(errorMsg);
        }
    }

    /**
     * 把布尔值转换为字节数组
     *
     * @param bool 布尔值
     * @return 字节数组
     */
    public static byte getByte(boolean bool) {
        return (byte) (bool ? 1 : 0);
    }

    /**
     * 把布尔值转换为字节数组
     *
     * @param bool 布尔值
     * @return 字节数组
     */
    public static byte[] getByteArr(boolean bool) {
        int boolValue = bool ? 1 : 0;
        return getByteArr(boolValue, BOOLEAN_BYTE_LENGTH);
    }

    /**
     * 把字符转换为字节数组
     *
     * @param c 字符
     * @return 字节数组
     */
    public static byte[] getByteArr(char c) {
        return getByteArr((short) c, CHAR_BYTE_LENGTH);
    }

    /**
     * 把short转换为字节数组
     *
     * @param s 布尔值
     * @return 字节数组
     */
    public static byte[] getByteArr(short s) {
        return getByteArr(s, SHORT_BYTE_LENGTH);
    }

    /**
     * 把int转换为字节数组
     *
     * @param i 整数值
     * @return 字节数组
     */
    public static byte[] getByteArr(int i) {
        return getByteArr(i, INT_BYTE_LENGTH);
    }

    /**
     * 把boolean转换为字节数组
     *
     * @param f 小数值
     * @return 字节数组
     */
    public static byte[] getByteArr(float f) {
        return getByteArr(Float.floatToIntBits(f), FLOAT_BYTE_LENGTH);
    }

    /**
     * 把long转换为字节数组
     *
     * @param l 整数值
     * @return 字节数组
     */
    public static byte[] getByteArr(long l) {
        return getByteArr(l, LONG_BYTE_LENGTH);
    }

    /**
     * 把boolean转换为字节数组
     *
     * @param d 小数值
     * @return 字节数组
     */
    public static byte[] getByteArr(double d) {
        return getByteArr(Double.doubleToLongBits(d), DOUBLE_BYTE_LENGTH);
    }

    /**
     * 获取字节数组，并且设置长度
     *
     * @param l         数字
     * @param arrLength 字节数组长度
     * @return 字节数组
     */
    private static byte[] getByteArr(long l, int arrLength) {
        // 声明字节掩码
        byte byteMask = (byte) 0xff;
        // 遍历掩码
        byte[] result = new byte[arrLength];
        for (int i = 0; i < arrLength; i++) {
            // 获取这一位的字节
            int offset = i * Byte.SIZE;
            int mask = byteMask << offset;

            // 倒序填充
            result[arrLength - 1 - i] = (byte) ((l & mask) >> offset);
        }

        return result;
    }

    /**
     * 把String转换为字节数组
     *
     * @param str 字符串
     * @return 字节数组
     */
    public static byte[] getByteArr(String str) {
        return getByteArr(str, StandardCharsets.UTF_8);
    }

    /**
     * 把String转换为字节数组
     *
     * @param str     字符串
     * @param charset 编码方式
     * @return 字节数组
     */
    public static byte[] getByteArr(String str, Charset charset) {
        return str.getBytes(charset);
    }

    /**
     * 把String转换为字节数组
     *
     * @param str         字符串
     * @param charsetName 编码方式
     * @return 字节数组
     */
    public static byte[] getByteArr(String str, String charsetName) {
        try {
            return str.getBytes(charsetName);
        } catch (UnsupportedEncodingException e) {
            throw new UnknownEncodingException(e);
        }
    }

    /**
     * 把可以序列化的对象变为字节数组
     *
     * @param serializable 可序列化的对象
     * @return byte数组
     */
    public static byte[] getByteArr(Serializable serializable) {
        return SerializationUtil.toByte(serializable);
    }

    /**
     * 把byte变为布尔值
     *
     * @param b byte
     * @return 布尔值
     */
    public static boolean parseToBoolean(byte b) {
        return b != 0;
    }

    /**
     * 把byte数组变为布尔值
     *
     * @param byteArr byte数组
     * @return 布尔值
     */
    public static boolean parseToBoolean(byte[] byteArr) {
        // 断言数组长度
        assertArrayLength(byteArr, BOOLEAN_BYTE_LENGTH);

        return byteArr[0] != 0;
    }

    /**
     * 把byte数组变为字符
     *
     * @param byteArr byte数组
     * @return 字符
     */
    public static char parseToChar(byte[] byteArr) {
        // 断言数组长度
        assertArrayLength(byteArr, CHAR_BYTE_LENGTH);
        return (char) parseToNum(byteArr);
    }

    /**
     * 把byte数组变为短整型
     *
     * @param byteArr byte数组
     * @return 短整型
     */
    public static short parseToShort(byte[] byteArr) {
        // 断言数组长度
        assertArrayLength(byteArr, SHORT_BYTE_LENGTH);
        return (short) parseToNum(byteArr);
    }

    /**
     * 把byte数组变为整型
     *
     * @param byteArr byte数组
     * @return 整型
     */
    public static int parseToInt(byte[] byteArr) {
        // 断言数组长度
        assertArrayLength(byteArr, INT_BYTE_LENGTH);
        return (int) parseToNum(byteArr);
    }

    /**
     * 把byte数组变为单精度浮点型
     *
     * @param byteArr byte数组
     * @return 单精度浮点型
     */
    public static float parseToFloat(byte[] byteArr) {
        // 断言数组长度
        assertArrayLength(byteArr, FLOAT_BYTE_LENGTH);
        return Float.intBitsToFloat((int) parseToNum(byteArr));
    }

    /**
     * 把byte数组变为长整型
     *
     * @param byteArr byte数组
     * @return 长整型
     */
    public static long parseToLong(byte[] byteArr) {
        // 断言数组长度
        assertArrayLength(byteArr, LONG_BYTE_LENGTH);
        return parseToNum(byteArr);
    }

    /**
     * 把byte数组变为双精度浮点型
     *
     * @param byteArr byte数组
     * @return 双精度浮点型
     */
    public static double parseToDouble(byte[] byteArr) {
        // 断言数组长度
        assertArrayLength(byteArr, DOUBLE_BYTE_LENGTH);
        return Double.longBitsToDouble(parseToNum(byteArr));
    }

    /**
     * 把byte数组变为字符串
     *
     * @param byteArr byte数组
     * @return 字符串
     */
    public static String parseToString(byte[] byteArr) {
        return parseToString(byteArr, StandardCharsets.UTF_8);
    }

    /**
     * 把byte数组变为字符串
     *
     * @param byteArr     byte数组
     * @param charsetName 编码方式
     * @return 字符串
     */
    public static String parseToString(byte[] byteArr, String charsetName) {
        try {
            return new String(byteArr, charsetName);
        } catch (UnsupportedEncodingException e) {
            throw new UnknownEncodingException(e);
        }
    }

    /**
     * 把byte数组变为字符串
     *
     * @param byteArr byte数组
     * @param charset 编码方式
     * @return 字符串
     */
    public static String parseToString(byte[] byteArr, Charset charset) {
        return new String(byteArr, charset);
    }

    /**
     * 把可以序列化的对象的字节数组反序列化
     * 变为对象
     *
     * @param byteArr byte数组
     * @return 对象
     */
    public static <T> T parseToObject(byte[] byteArr) {
        return SerializationUtil.toObj(byteArr);
    }

    /**
     * 断言数组长度
     *
     * @param byteArr      数组
     * @param targetLength 目标长度
     */
    private static void assertArrayLength(byte[] byteArr, int targetLength) {
        if (byteArr.length != targetLength) {
            throw new ArrayLengthIllegalException(byteArr.length, targetLength);
        }
    }

    /**
     * 把byte数组变为数字
     *
     * @param byteArr byte数组
     * @return 字符
     */
    private static long parseToNum(byte[] byteArr) {
        // 字节掩码
        int bitMask = 0XFF;

        long sum = 0;
        for (int i = 0; i < byteArr.length; i++) {
            // 倒序遍历
            byte b = byteArr[byteArr.length - 1 - i];
            // 移位，位或
            sum |= (long) (b & bitMask) << (i * Byte.SIZE);
        }
        return sum;
    }
}
